package com.ruoyi.factorydata.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.annotation.DataSource;
import com.ruoyi.common.utils.*;
import com.ruoyi.factorydata.domain.*;
import com.ruoyi.factorydata.domain.dto.*;
import com.ruoyi.factorydata.domain.vo.CraftRouteVo;
import com.ruoyi.factorydata.mapper.*;
import com.ruoyi.factorydata.service.*;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.system.service.ISysUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 工单信息Service业务层处理
 *
 * @author ruoyi
 * @date 2025-07-27
 */
@Slf4j
@Service
@DataSource
public class WorkOrderInfoServiceImpl extends ServiceImpl<WorkOrderInfoMapper, WorkOrderInfo> implements IWorkOrderInfoService {
    private final WorkOrderInfoMapper workOrderInfoMapper;
    private final ProductionLineDataMapper productionLineDataMapper;
    private final WorkshopDataMapper workshopDataMapper;
    private final ProjectProductionPlanMapper projectProductionPlanMapper;
    private final ICraftRouteService craftRouteService;
    private final ISysUserService sysUserService;
    private final ProductionProjectMapper productionProjectMapper;
    private final ModelInventoryMapper modelInventoryMapper;
    private final MaterialDataMapper materialDataMapper;
    private final CraftProcessRelationMapper craftProcessRelationMapper;
    private final CraftProcessMaterialRelationMapper craftProcessMaterialRelationMapper;
    private final MaterialInventoryMapper materialInventoryMapper;
    private final MaterialRequisitionMapper materialRequisitionMapper;
    private final MaterialRequisitionDetailMapper materialRequisitionDetailMapper;
    private final ProcessDataMapper processDataMapper;
    private final WorkReportMapper workReportMapper;
    private final IWorkOrderLogService workOrderLogService;
    private final CustomerDataMapper customerDataMapper;
    private final MaterialCategoryMapper materialCategoryMapper;
    private final WorkOrderProcessInfoMapper workOrderProcessInfoMapper;
    private final PrintTemplateMapper printTemplateMapper;
    private final ProductionBomMapper productionBomMapper;
    private final ProductionBomMaterialMapper productionBomMaterialMapper;
    private final MaterialUnitDataMapper materialUnitDataMapper;
    private final EquipmentDataMapper equipmentDataMapper;
    private final IWarehousePositionService warehousePositionService;
    private final IWarehouseInfoService warehouseInfoService;

    private final ISysConfigService sysConfigService;


    @Lazy
    public WorkOrderInfoServiceImpl(WorkOrderInfoMapper workOrderInfoMapper,
                                    ProductionLineDataMapper productionLineDataMapper,
                                    WorkshopDataMapper workshopDataMapper,
                                    ProjectProductionPlanMapper projectProductionPlanMapper,
                                    ICraftRouteService craftRouteService,
                                    ISysUserService sysUserService,
                                    ProductionProjectMapper productionProjectMapper,
                                    ModelInventoryMapper modelInventoryMapper,
                                    MaterialDataMapper materialDataMapper,
                                    CraftProcessRelationMapper craftProcessRelationMapper,
                                    CraftProcessMaterialRelationMapper craftProcessMaterialRelationMapper,
                                    MaterialInventoryMapper materialInventoryMapper,
                                    MaterialRequisitionMapper materialRequisitionMapper,
                                    MaterialRequisitionDetailMapper materialRequisitionDetailMapper,
                                    ProcessDataMapper processDataMapper,
                                    WorkReportMapper workReportMapper,
                                    IWorkOrderLogService workOrderLogService,
                                    CustomerDataMapper customerDataMapper,
                                    MaterialCategoryMapper materialCategoryMapper,
                                    WorkOrderProcessInfoMapper workOrderProcessInfoMapper,
                                    PrintTemplateMapper printTemplateMapper,
                                    ProductionBomMapper productionBomMapper,
                                    ProductionBomMaterialMapper productionBomMaterialMapper,
                                    MaterialUnitDataMapper materialUnitDataMapper,
                                    EquipmentDataMapper equipmentDataMapper,
                                    IWarehousePositionService warehousePositionService,
                                    IWarehouseInfoService warehouseInfoService,
                                    ISysConfigService sysConfigService) {
        this.workOrderInfoMapper = workOrderInfoMapper;
        this.productionLineDataMapper = productionLineDataMapper;
        this.workshopDataMapper = workshopDataMapper;
        this.projectProductionPlanMapper = projectProductionPlanMapper;
        this.craftRouteService = craftRouteService;
        this.sysUserService = sysUserService;
        this.productionProjectMapper = productionProjectMapper;
        this.modelInventoryMapper = modelInventoryMapper;
        this.materialDataMapper = materialDataMapper;
        this.craftProcessRelationMapper = craftProcessRelationMapper;
        this.craftProcessMaterialRelationMapper = craftProcessMaterialRelationMapper;
        this.materialInventoryMapper = materialInventoryMapper;
        this.materialRequisitionMapper = materialRequisitionMapper;
        this.materialRequisitionDetailMapper = materialRequisitionDetailMapper;
        this.processDataMapper = processDataMapper;
        this.workReportMapper = workReportMapper;
        this.workOrderLogService = workOrderLogService;
        this.customerDataMapper = customerDataMapper;
        this.materialCategoryMapper = materialCategoryMapper;
        this.workOrderProcessInfoMapper = workOrderProcessInfoMapper;
        this.printTemplateMapper = printTemplateMapper;
        this.productionBomMapper = productionBomMapper;
        this.productionBomMaterialMapper = productionBomMaterialMapper;
        this.materialUnitDataMapper = materialUnitDataMapper;
        this.equipmentDataMapper = equipmentDataMapper;
        this.warehousePositionService = warehousePositionService;
        this.warehouseInfoService = warehouseInfoService;
        this.sysConfigService = sysConfigService;
    }

    /**
     * 查询工单信息
     *
     * @param id 工单信息主键
     * @return 工单信息
     */
    @Override
    public WorkOrderInfo selectWorkOrderInfoById(Long id) {
        Map<Long, String> sysUserCache = new HashMap<>();
        WorkOrderInfo workOrderInfo = workOrderInfoMapper.selectWorkOrderInfoById(id);
        Assert.notNull(workOrderInfo, "工单不存在");
        MaterialData materialData = materialDataMapper.selectById(workOrderInfo.getMaterialId());
        if (materialData != null) {
            workOrderInfo.setMaterialImages(materialData.getMaterialImages());
        }
        ProductionProject productionProject = productionProjectMapper.selectProductionProjectById(workOrderInfo.getProjectId());
        if (productionProject != null) {
            workOrderInfo.setProjectManagerName(sysUserService.getUserNameByIdAndCache(productionProject.getProjectManager(), sysUserCache));
            workOrderInfo.setProjectManagerId(productionProject.getProjectManager());
            workOrderInfo.setSurfaceRequirement(productionProject.getSurfaceRequirement());
            workOrderInfo.setCharacterizationRequirement(productionProject.getCharacterizationRequirement());
            workOrderInfo.setTarget(productionProject.getTarget());
            workOrderInfo.setProjectAttachments(productionProject.getAttachments());
        }
        workOrderInfo.setWorkOrderOwnerName(sysUserService.getUserNameByIdAndCache(workOrderInfo.getWorkOrderOwnerId(), sysUserCache));
        if (workOrderInfo.getModelMaterialCode() != null) {
            ModelInventory modelInventory = modelInventoryMapper.selectModelByCode(workOrderInfo.getModelMaterialCode());
            workOrderInfo.setModelInventory(modelInventory);
        }
        CraftRouteVo craftRoute = craftRouteService.selectCraftRouteById(workOrderInfo.getCraftRouteId() + "");
        if (craftRoute != null) {
            workOrderInfo.setCraftRouteName(craftRoute.getRouteName());
            workOrderInfo.setCraftRoute(craftRoute);
        }
        if (workOrderInfo.getCustomerId() != null) {
            CustomerData customerData = customerDataMapper.selectCustomerDataById(workOrderInfo.getCustomerId());
            if (customerData != null) {
                workOrderInfo.setCustomerName(customerData.getCustomerName());
                workOrderInfo.setCustomerCode(customerData.getCustomerCode());
            }
        }
        if (workOrderInfo.getMaterialCategoryId() != null) {
            MaterialCategory materialCategoryData = materialCategoryMapper.selectMaterialCategoryById(workOrderInfo.getMaterialCategoryId());
            if (materialCategoryData != null) {
                workOrderInfo.setMaterialCategoryName(materialCategoryData.getMaterialCategoryName());
            }
        }
        if (workOrderInfo.getNowProcessOrderOwnerId() != null) {
            workOrderInfo.setNowProcessOrderOwnerName(sysUserService.getUserNameByIdAndCache(workOrderInfo.getNowProcessOrderOwnerId(), sysUserCache));
        }
        return workOrderInfo;
    }

    @Override
    public List<WorkOrderStatisticsDTO> statisticsList(WorkOrderInfo workOrderInfo) {
        List<WorkOrderStatisticsDTO> workOrderInfos = workOrderInfoMapper.runWorkOrderList(workOrderInfo);
        HashMap<Long, String> customCache = new HashMap<>();
        for (WorkOrderStatisticsDTO orderInfo : workOrderInfos) {
            Integer status = orderInfo.getStatus();
            Long completedQuantity = Optional.ofNullable(orderInfo.getCompletedQuantity()).orElse(0L);
            orderInfo.setCompletedQuantity(completedQuantity);
            //工单完结有报废数量
            if (status == 3 || status == 4) {
                long scrapQuantity = orderInfo.getPlannedQuantity() - completedQuantity;
                orderInfo.setScrapQuantity(scrapQuantity > 0 ? scrapQuantity : 0L);
            }
            Integer craftRouteProcessCount = craftProcessRelationMapper.selectCount(new LambdaQueryWrapper<CraftProcessRelation>()
                    .eq(CraftProcessRelation::getCraftId, orderInfo.getCraftRouteId())
                    .eq(CraftProcessRelation::getDelFlag, 0)
            );
            //工艺路线工序总数
            orderInfo.setCraftRouteProcessCount(Optional.ofNullable(craftRouteProcessCount).orElse(0));
            //工单时长=工单结束时间-工单开始时间
            Date startTime = Optional.ofNullable(orderInfo.getActualStartTime()).orElse(DateUtils.getNowDate());
            Date endTime = Optional.ofNullable(orderInfo.getActualEndTime()).orElse(DateUtils.getNowDate());
            orderInfo.setWorkOrderDuration(DateUtils.differentDaysByMillisecond(endTime, startTime) + 1);
            //工单延期天数=工单结束日期-计划结束日期，如果当前时间大于工单预计结束时间的话
            if (endTime.after(orderInfo.getPlannedEndTime())) {
                orderInfo.setDelayDays(DateUtils.differentDaysByMillisecond(orderInfo.getPlannedEndTime(), endTime));
            }
            orderInfo.setCustomerCode(CacheSupportUtils.getFromCacheOrDB(orderInfo.getCustomerId(), customCache, customerDataMapper::selectCustomerDataById, CustomerData::getCustomerCode, false));
        }
        return workOrderInfos;
    }

    @Override
    public List<ProductStatisticsDTO> statisticsProductList(WorkOrderInfo workOrderInfo) {
        return workOrderInfoMapper.statisticsProductList(workOrderInfo);
    }

    /**
     * 查询工单信息列表
     *
     * @param workOrderInfo 工单信息
     * @return 工单信息
     */
    @Override
    public List<WorkOrderInfo> selectWorkOrderInfoList(WorkOrderInfo workOrderInfo) {
        List<WorkOrderInfo> workOrderInfos = workOrderInfoMapper.selectWorkOrderInfoList(workOrderInfo);
        Map<Long, String> craftRouteNameMap = new HashMap<>();
        Map<Long, String> createByCache = new HashMap<>();
        Map<Long, String> customCache = new HashMap<>();
        Map<Long, Integer> projectRunWorkCountCache = new HashMap<>();
        workOrderInfos.forEach(workOrder -> {
            String craftRouteName = craftRouteService.getNameByIdAndCache(workOrder.getCraftRouteId(), craftRouteNameMap);
            String createByName = sysUserService.getUserNameByIdAndCache(Long.parseLong(workOrder.getCreateBy()), createByCache);
            //当前工单
            Integer projectRunWorkOrderNum = getWorkOrderRunNumByProjectAndCache(workOrder.getProjectId(), projectRunWorkCountCache);
            workOrder.setCraftRouteName(craftRouteName);
            workOrder.setWorkOrderOwnerName(sysUserService.getUserNameByIdAndCache(workOrder.getWorkOrderOwnerId(), createByCache));
            workOrder.setCreateByName(createByName);
            workOrder.setProjectRunWorkOrderNum(projectRunWorkOrderNum);
            workOrder.setCustomerCode(CacheSupportUtils.getFromCacheOrDB(workOrder.getCustomerId(), customCache, customerDataMapper::selectCustomerDataById, CustomerData::getCustomerCode, false));
        });
        return workOrderInfos;
    }

    @Override
    public List<WorkOrderInfo> selectWorkOrderInfoTaskList(WorkOrderInfo workOrderInfo) {
        workOrderInfo.setIsTask(1);
        List<WorkOrderInfo> workOrderInfos = workOrderInfoMapper.selectWorkOrderInfoList(workOrderInfo);
        Map<Long, String> craftRouteNameMap = new HashMap<>();
        Map<Long, String> createByCache = new HashMap<>();
        Map<Long, String> customCache = new HashMap<>();
        Map<Long, Integer> projectRunWorkCountCache = new HashMap<>();
        workOrderInfos.forEach(workOrder -> {
            String craftRouteName = craftRouteService.getNameByIdAndCache(workOrder.getCraftRouteId(), craftRouteNameMap);
            String createByName = sysUserService.getUserNameByIdAndCache(Long.parseLong(workOrder.getCreateBy()), createByCache);
            //当前工单
            Integer projectRunWorkOrderNum = getWorkOrderRunNumByProjectAndCache(workOrder.getProjectId(), projectRunWorkCountCache);
            workOrder.setCraftRouteName(craftRouteName);
            workOrder.setCreateByName(createByName);
            workOrder.setWorkOrderOwnerName(sysUserService.getUserNameByIdAndCache(workOrder.getWorkOrderOwnerId(), createByCache));
            workOrder.setProjectRunWorkOrderNum(projectRunWorkOrderNum);
            workOrder.setCustomerCode(CacheSupportUtils.getFromCacheOrDB(workOrder.getCustomerId(), customCache, customerDataMapper::selectCustomerDataById, CustomerData::getCustomerCode, false));

        });
        return workOrderInfos;
    }

    /**
     * 新增工单信息
     *
     * @param workOrderInfo 工单信息
     * @return 结果
     */
    @Override
    @Transactional
    public int insertWorkOrderInfo(WorkOrderInfo workOrderInfo) {
        Assert.notNull(workOrderInfo, "参数不能为空");
        Assert.notNull(workOrderInfo.getProductionLineId(), "请选择生产线");
        Assert.notNull(workOrderInfo.getWorkshopId(), "请选择车间");
        workOrderInfo.setCreateTime(DateUtils.getNowDate());
        workOrderInfo.setCreateBy(SecurityUtils.getUserId() + "");
        //工单编号是否存在
        List<WorkOrderInfo> workOrderInfos = selectWorkOrderInfoList(WorkOrderInfo.builder().workOrderCode(workOrderInfo.getWorkOrderCode()).build());
        Assert.isTrue(workOrderInfos.size() == 0, "工单编号已存在");
        //生产线名称
        ProductionLineData productionLineData = productionLineDataMapper.selectById(workOrderInfo.getProductionLineId());
        Assert.notNull(productionLineData, "生产线不存在");
        workOrderInfo.setProductionLineName(productionLineData.getName());
        WorkshopData workshopData = workshopDataMapper.selectById(workOrderInfo.getWorkshopId());
        Assert.notNull(workshopData, "车间不存在");
        workOrderInfo.setWorkshopName(workshopData.getName());
        int save = workOrderInfoMapper.insertWorkOrderInfo(workOrderInfo);
        //更新计划状态
        if (save > 0) {
            projectProductionPlanMapper.updateProjectProductionPlan(ProjectProductionPlan.builder()
                    .id(workOrderInfo.getPlanId())
                    .status("3")//已生成
                    .build());
            workOrderLogService.saveLog(workOrderInfo, "工单生成");
        }
        return save;
    }

    /**
     * 修改工单信息
     *
     * @param workOrderInfo 工单信息
     * @return 结果
     */
    @Override
    @Transactional
    public int updateWorkOrderInfo(WorkOrderInfo workOrderInfo) {
        workOrderInfo.setUpdateTime(DateUtils.getNowDate());
        if (workOrderInfo.getStatus() != null && workOrderInfo.getStatus() > 1) {
            throw new RuntimeException("工单状态不能修改");
        }
        int update = workOrderInfoMapper.updateWorkOrderInfo(workOrderInfo);
        if (update > 0) {
            workOrderLogService.saveLog(workOrderInfo, "工单下发");
        }
        return update;
    }

    /**
     * 批量删除工单信息
     *
     * @param ids 需要删除的工单信息主键
     * @return 结果
     */
    @Override
    public int deleteWorkOrderInfoByIds(Long[] ids) {
        return workOrderInfoMapper.deleteWorkOrderInfoByIds(ids);
    }

    /**
     * 删除工单信息信息
     *
     * @param id 工单信息主键
     * @return 结果
     */
    @Override
    public int deleteWorkOrderInfoById(Long id) {
        return workOrderInfoMapper.deleteWorkOrderInfoById(id);
    }

    @Override
    public Integer getWorkOrderRunNumByProject(Long projectId) {
        return lambdaQuery().eq(WorkOrderInfo::getProjectId, projectId)
                .in(WorkOrderInfo::getStatus, 1, 2)//待生产 生产中
                .count();
    }

    @Override
    public Integer getWorkOrderRunNumByProjectAndCache(Long projectId, Map<Long, Integer> projectRunNumCache) {
        return CacheSupportUtils.getFromCacheOrDB(projectId, projectRunNumCache, this::getWorkOrderRunNumByProject, num -> num, true);
    }

    @Override
    @Transactional
    public int startWorkOrderInfo(Long workOrderId) {
        WorkOrderInfo workOrderInfo = getById(workOrderId);
        Assert.notNull(workOrderInfo, "工单不存在");
        Assert.isTrue(workOrderInfo.getStatus() == 1, "工单状态异常");
        //过去工艺路线第一个工序
        CraftProcessRelation craftProcessRelation = craftProcessRelationMapper.selectProcessStep(workOrderInfo.getCraftRouteId(), 1);
        Assert.notNull(craftProcessRelation, "工艺路线不存在");

        Long userId = SecurityUtils.getUserId();
        Long workOrderOwnerId = workOrderInfo.getWorkOrderOwnerId();
        Long processOrderOwnerId = craftProcessRelation.getProcessOrderOwnerId();
        boolean isProcessOwner = processOrderOwnerId != null && Objects.equals(userId, processOrderOwnerId);
        //工单负责人和工序负责人才可以开启工单
        Assert.isTrue(workOrderOwnerId.equals(userId) || isProcessOwner, "您没有权限开始工单");

        workOrderInfo.setNowProcessId(craftProcessRelation.getProcessId());
        workOrderInfo.setNowProcessStep(1);
        workOrderInfo.setNowProcessName(craftProcessRelation.getProcessName());
        workOrderInfo.setNowProcessCode(craftProcessRelation.getProcessCode());
        workOrderInfo.setNowProcessType(craftProcessRelation.getProcessTypeId());
        workOrderInfo.setNowProcessOrderOwnerId(craftProcessRelation.getProcessOrderOwnerId());
        workOrderInfo.setStatus(2);
        workOrderInfo.setActualStartTime(DateUtils.getNowDate());
        int update = workOrderInfoMapper.updateWorkOrderInfo(workOrderInfo);
        if (update > 0) {
            WorkOrderProcessInfo workOrderProcessInfo = WorkOrderProcessInfo.builder()
                    .projectId(workOrderInfo.getProjectId())
                    .projectCode(workOrderInfo.getProjectCode())
                    .planId(workOrderInfo.getPlanId())
                    .planCode(workOrderInfo.getPlanCode())
                    .workOrderCode(workOrderInfo.getWorkOrderCode())
                    .workOrderId(workOrderInfo.getId())
                    .craftId(craftProcessRelation.getCraftId())
                    .step(craftProcessRelation.getStep().intValue())
                    .processId(craftProcessRelation.getProcessId())
                    .processCode(craftProcessRelation.getProcessCode())
                    .processName(craftProcessRelation.getProcessName())
                    .processOrderOwnerId(craftProcessRelation.getProcessOrderOwnerId())
                    .startTime(DateUtils.getNowDate())
                    .build();
            workOrderProcessInfo.setCreateBy(userId + "");
            workOrderProcessInfo.setCreateTime(DateUtils.getNowDate());
            workOrderProcessInfoMapper.insertWorkOrderProcessInfo(workOrderProcessInfo);
            workOrderLogService.saveLog(workOrderInfo, "工单启动");
            //计划完成
            projectProductionPlanMapper.updateProjectProductionPlan(ProjectProductionPlan.builder()
                    .id(workOrderInfo.getPlanId())
                    .actualStartTime(new Date())
                    .build());
        }
        return update;
    }

    @Override
    public int suspendWorkOrderInfo(Long workOrderId) {
        WorkOrderInfo workOrderInfo = getById(workOrderId);
        Assert.notNull(workOrderInfo, "工单不存在");
        Assert.isTrue(workOrderInfo.getStatus() == 1 || workOrderInfo.getStatus() == 2, "工单状态异常");

        Long userId = SecurityUtils.getUserId();
        Long workOrderOwnerId = workOrderInfo.getWorkOrderOwnerId();
        boolean isAdmin = SecurityUtils.hasRole("admin");
        //查询项目信息
        Long projectManager = null;
        if (workOrderInfo.getProjectId() != null) {
            ProductionProject productionProject = productionProjectMapper.selectById(workOrderInfo.getProjectId());
            projectManager = productionProject.getProjectManager();
        }
        if (!isAdmin && !Objects.equals(userId, workOrderOwnerId)) {
            Assert.isTrue(projectManager != null && projectManager.equals(userId), "您没有权限操作工单");
        }
        workOrderInfo.setSuspendStatus(workOrderInfo.getStatus());
        workOrderInfo.setStatus(5);
        workOrderInfo.setUpdateBy(userId + "");
        workOrderInfo.setUpdateTime(new Date());
        int update = workOrderInfoMapper.updateWorkOrderInfo(workOrderInfo);
        if (update > 0) {
            //工序信息转移负责人
            HashMap<Long, String> nameCache = new HashMap<>();
            String optName = sysUserService.getUserNameByIdAndCache(userId, nameCache);
            //保存日志
            workOrderLogService.saveLog(workOrderInfo, "工单暂停：操作人`" + optName);
        }
        return update;
    }

    @Override
    public int cancelSuspendWorkOrderInfo(Long workOrderId) {
        WorkOrderInfo workOrderInfo = getById(workOrderId);
        Assert.notNull(workOrderInfo, "工单不存在");
        Assert.isTrue(workOrderInfo.getStatus() == 5, "工单状态异常");

        Long userId = SecurityUtils.getUserId();
        Long workOrderOwnerId = workOrderInfo.getWorkOrderOwnerId();
        boolean isAdmin = SecurityUtils.hasRole("admin");
        //查询项目信息
        Long projectManager = null;
        if (workOrderInfo.getProjectId() != null) {
            ProductionProject productionProject = productionProjectMapper.selectById(workOrderInfo.getProjectId());
            projectManager = productionProject.getProjectManager();
        }
        if (!isAdmin && !Objects.equals(userId, workOrderOwnerId)) {
            Assert.isTrue(projectManager != null && projectManager.equals(userId), "您没有权限操作工单");
        }
        workOrderInfo.setStatus(workOrderInfo.getSuspendStatus());
        workOrderInfo.setUpdateBy(userId + "");
        workOrderInfo.setUpdateTime(new Date());
        int update = workOrderInfoMapper.updateWorkOrderInfo(workOrderInfo);
        if (update > 0) {
            HashMap<Long, String> nameCache = new HashMap<>();
            String optName = sysUserService.getUserNameByIdAndCache(userId, nameCache);
            //保存日志
            workOrderLogService.saveLog(workOrderInfo, "工单取消暂停：操作人`" + optName);
        }
        return update;
    }

    @Override
    public List<CraftProcessMaterialRelation> getMaterial(Long workOrderId) {
        Assert.notNull(workOrderId, "参数不能为空");
        WorkOrderInfo workOrderInfo = getById(workOrderId);
        Assert.notNull(workOrderInfo, "工单不存在");
        //获取全部工序
        List<CraftProcessMaterialRelation> craftProcessMaterialRelations = craftProcessMaterialRelationMapper.selectCraftProcessMaterialRelationList(
                CraftProcessMaterialRelation
                        .builder()
                        .craftId(workOrderInfo.getCraftRouteId())
                        .build()
        );
        Map<String, CraftProcessMaterialRelation> materialRelationMap = craftProcessMaterialRelations.stream().collect(
                Collectors.toMap(
                        CraftProcessMaterialRelation::getMaterialCode,
                        item -> item,
                        (k1, k2) -> k1
                ));
        //craftProcessMaterialRelations 根据物料id 计算出每种物料的materialUsedQuantity之和
        Map<Long, Long> materialUsedQuantitySum = craftProcessMaterialRelations.stream()
                .collect(Collectors.groupingBy(
                        CraftProcessMaterialRelation::getMaterialId,
                        Collectors.summingLong(CraftProcessMaterialRelation::getMaterialUsedQuantity)
                ));

        //获取所有value
        List<CraftProcessMaterialRelation> relationList = new ArrayList<>(materialRelationMap.values())
                .stream()
                .peek(material -> {
                    material.setIsMaterial(1);
                    Long materialInventory = materialInventoryMapper.countMaterialInventoryList(MaterialInventory.builder()
                            .materialCode(material.getMaterialCode())
                            .build());
                    material.setMaterialInventory(Optional.ofNullable(materialInventory).orElse(0L));
                    Long appliedNum = materialRequisitionDetailMapper.countByWorkOrderIdAndMaterialId(workOrderId, material.getMaterialId());
                    material.setAppliedNum(Optional.ofNullable(appliedNum).orElse(0L));
                    Long useNum = materialUsedQuantitySum.get(material.getMaterialId());
                    material.setMaterialUsedQuantity(Optional.ofNullable(useNum).orElse(0L).intValue());
                })
                .collect(Collectors.toList());
        //使用了母版
        if (StringUtils.isNotBlank(workOrderInfo.getModelMaterialCode())) {
            List<ModelInventory> modelInventories = modelInventoryMapper.selectModelCodeList(ModelInventory.builder().modelNumber(workOrderInfo.getModelMaterialCode()).build());
            ModelInventory modelInventory = modelInventories.get(0);
            Long appliedNum = materialRequisitionDetailMapper.countByWorkOrderIdAndModelCode(workOrderId, workOrderInfo.getModelMaterialCode());
            //列表第一个插入母版信息
            relationList.add(0, CraftProcessMaterialRelation.builder()
                    .isMaterial(0)
                    .modelInventory(modelInventory)
                    .materialInventory(modelInventory.getStockQuantity())
                    .appliedNum(Optional.ofNullable(appliedNum).orElse(0L))
                    .materialUsedQuantity(1)
                    .build());
        }
        return relationList;

    }

    @Override
    @Transactional
    public int applyMaterial(Long workOrderId, List<CraftProcessMaterialRelation> materialRequisition) {
        Assert.notNull(workOrderId, "参数不能为空");
        WorkOrderInfo workOrderInfo = getById(workOrderId);
        Assert.isTrue(workOrderInfo.getStatus() == 2, "工单状态异常");
        Long userId = SecurityUtils.getUserId();
        Long workOrderOwnerId = workOrderInfo.getWorkOrderOwnerId();
        boolean isAdmin = SecurityUtils.hasRole("admin");
        //查询项目信息
        Long projectManager = null;
        if (workOrderInfo.getProjectId() != null) {
            ProductionProject productionProject = productionProjectMapper.selectById(workOrderInfo.getProjectId());
            projectManager = productionProject.getProjectManager();
        }
        if (!isAdmin && !Objects.equals(userId, workOrderOwnerId)) {
            //过去工艺路线第一个工序
            Integer nowProcessStep = workOrderInfo.getNowProcessStep();
            nowProcessStep=Optional.ofNullable(nowProcessStep).orElse(1);
            CraftProcessRelation craftProcessRelation = craftProcessRelationMapper.selectProcessStep(workOrderInfo.getCraftRouteId(), nowProcessStep);
            Assert.notNull(craftProcessRelation, "工艺路线不存在");

            Long processOrderOwnerId = craftProcessRelation.getProcessOrderOwnerId();
            boolean isProcessOwner = processOrderOwnerId != null && Objects.equals(userId, processOrderOwnerId);
            if(!isProcessOwner){
                Assert.isTrue(projectManager != null && projectManager.equals(userId), "您没有权限领料");
            }
        }
        //过滤申请为0的数据
        materialRequisition = materialRequisition.stream().filter(item -> item.getApplyNum() > 0).collect(Collectors.toList());
        MaterialRequisition requisition = new MaterialRequisition();
        requisition.setRequisitionNo("MRQ" + DateFormatUtils.format(new Date(), "yyyyMMddHHmmss"));
        requisition.setWorkOrderId(workOrderInfo.getId());
        requisition.setProjectId(workOrderInfo.getProjectId());
        requisition.setPlanId(workOrderInfo.getPlanId());
        requisition.setRequisitionType(1);
        requisition.setRequisitionUserId(userId);
        requisition.setRequisitionTime(null);
        requisition.setStatus(0);
        requisition.setCreateBy(SecurityUtils.getUserId() + "");
        int save = materialRequisitionMapper.insertMaterialRequisition(requisition);
        if (save > 0) {
            materialRequisition.forEach(item -> {
                Long materialInventory = 0L;
                if (StringUtils.isBlank(item.getModelSource())) {
                    materialInventory = materialInventoryMapper.countMaterialInventoryList(MaterialInventory.builder()
                            .materialId(item.getMaterialId())
                            .build());
                } else {
                    materialInventory = modelInventoryMapper.countModelInventoryList(ModelInventory.builder()
                            .modelNumber(item.getModelNumber())
                            .build());
                }
                MaterialRequisitionDetail materialRequisitionDetail = MaterialRequisitionDetail.builder()
                        .requisitionId(requisition.getId())
                        .requisitionNo(requisition.getRequisitionNo())
                        .projectId(workOrderInfo.getProjectId())
                        .planId(workOrderInfo.getPlanId())
                        .workOrderId(workOrderInfo.getId())
                        .type(StringUtils.isBlank(item.getModelSource()) ? 1 : 2)
                        .materialId(item.getMaterialId())
                        .materialCode(item.getMaterialCode())
                        .materialName(item.getMaterialName())
                        .materialType(item.getMaterialTypeId())
                        .materialSpecification(item.getMaterialSpecification())
                        .materialUnit(item.getMaterialUnitId())
                        .materialUnitName(item.getMaterialUnitName())
                        .materialCategoryId(item.getMaterialCategoryId())
                        .manufacturingMethod(item.getManufacturingMethodId())
                        .modelSource(item.getModelSource())
                        .modelNumber(item.getModelNumber())
                        .requiredQuantity(BigDecimal.valueOf(item.getApplyNum()))
                        .inventoryQuantity(BigDecimal.valueOf(Optional.ofNullable(materialInventory).orElse(0L)))
                        .receivedQuantity(BigDecimal.ZERO)
                        .shortageQuantity(BigDecimal.ZERO)
                        .returnedQuantity(BigDecimal.ZERO)
                        .build();
                materialRequisitionDetail.setCreateBy(requisition.getCreateBy());
                materialRequisitionDetailMapper.insertMaterialRequisitionDetail(materialRequisitionDetail);
            });
            workOrderLogService.saveLog(workOrderInfo, "工单申请领料，领料单号：" + requisition.getRequisitionNo());
        }
        return save;
    }

    @Override
    public int returnMaterial(Long workOrderId, MaterialRequisitionDetail materialRequisitionDetail) {
        Assert.notNull(workOrderId, "参数不能为空");
        Assert.notNull(materialRequisitionDetail.getId(), "参数不能为空");
        Assert.isTrue(materialRequisitionDetail.getReturnedQuantity().compareTo(BigDecimal.ZERO) >= 0, "退料数量不能小于0");
        WorkOrderInfo workOrderInfo = getById(workOrderId);
        Assert.isTrue(workOrderInfo.getStatus() == 3||workOrderInfo.getStatus() == 4, "工单状态异常");
        Long userId = SecurityUtils.getUserId();
        Long workOrderOwnerId = workOrderInfo.getWorkOrderOwnerId();
        boolean isAdmin = SecurityUtils.hasRole("admin");
        //查询项目信息
        Long projectManager = null;
        if (workOrderInfo.getProjectId() != null) {
            ProductionProject productionProject = productionProjectMapper.selectById(workOrderInfo.getProjectId());
            projectManager = productionProject.getProjectManager();
        }
        if (!isAdmin && !Objects.equals(userId, workOrderOwnerId)) {
            Integer nowProcessStep = workOrderInfo.getNowProcessStep();
            nowProcessStep=Optional.ofNullable(nowProcessStep).orElse(1);
            CraftProcessRelation craftProcessRelation = craftProcessRelationMapper.selectProcessStep(workOrderInfo.getCraftRouteId(), nowProcessStep);
            Assert.notNull(craftProcessRelation, "工艺路线不存在");

            Long processOrderOwnerId = craftProcessRelation.getProcessOrderOwnerId();
            boolean isProcessOwner = processOrderOwnerId != null && Objects.equals(userId, processOrderOwnerId);
            if(!isProcessOwner){
                Assert.isTrue(projectManager != null && projectManager.equals(userId), "您没有权限申请退料");
            }
        }
        MaterialRequisitionDetail requisitionDetail = materialRequisitionDetailMapper.selectMaterialRequisitionDetailById(materialRequisitionDetail.getId());
        Assert.isTrue(requisitionDetail.getWorkOrderId().equals(workOrderId), "领料数据不存在");
        Assert.isTrue(requisitionDetail.getApplyReturnedQuantity() == null || requisitionDetail.getApplyReturnedQuantity().compareTo(BigDecimal.ZERO) == 0,
                "存在退料中数据");

        BigDecimal returnAllNum = requisitionDetail.getReturnedQuantity().add(materialRequisitionDetail.getReturnedQuantity());
        Assert.isTrue(returnAllNum.compareTo(requisitionDetail.getRequiredQuantity()) <= 0, "退料数量不能大于需求数量");

        MaterialRequisition materialRequisition = materialRequisitionMapper.selectMaterialRequisitionById(requisitionDetail.getRequisitionId());
        materialRequisition.setStatus(3);//有退料
        materialRequisition.setUpdateBy(SecurityUtils.getUserId() + "");
        int update = materialRequisitionMapper.updateMaterialRequisition(materialRequisition);
        if (update > 0) {
            requisitionDetail.setApplyReturnedQuantity(materialRequisitionDetail.getReturnedQuantity());
            requisitionDetail.setReturnedQuantity(returnAllNum);
            requisitionDetail.setUpdateBy(SecurityUtils.getUserId() + "");
            update = materialRequisitionDetailMapper.updateMaterialRequisitionDetail(requisitionDetail);
            Assert.isTrue(update > 0, "退料保存失败");
            String materialName = requisitionDetail.getType() == 1 ? "物料：" + materialRequisitionDetail.getMaterialCode() : "母版：" + requisitionDetail.getModelNumber();
            workOrderLogService.saveLog(workOrderInfo, "工单申请退料，" + materialName + ",退料数量：" + materialRequisitionDetail.getReturnedQuantity());
        }
        return update;
    }


    @Override
    public int next(Long workOrderId, Integer actualQuantity, Integer scrapQuantity) {
        Assert.notNull(workOrderId, "参数不能为空");
        WorkOrderInfo workOrderInfo = getById(workOrderId);
        Assert.isTrue(workOrderInfo.getStatus() == 2, "工单状态异常");
        Long userId = SecurityUtils.getUserId();
        Long workOrderOwnerId = workOrderInfo.getWorkOrderOwnerId();
        //工单负责人和当前工序负责人才可以完工
        Assert.isTrue(workOrderOwnerId.equals(userId) || Objects.equals(workOrderInfo.getNowProcessOrderOwnerId(), userId), "您没有权限操作");
        //当前工序是否需要报工
        ProcessData processData = processDataMapper.selectProcessDataById(workOrderInfo.getNowProcessId());
        Assert.isTrue(processData != null, "当前工序不存在");
        if (Integer.parseInt(Optional.ofNullable(processData.getWorkOrderReporting()).orElse("0")) == 1) {
            //强制要求报工
            Integer count = workReportMapper.selectCount(new LambdaQueryWrapper<WorkReport>()
                    .eq(WorkReport::getWorkOrderId, workOrderId)
                    .eq(WorkReport::getNowProcessStep, workOrderInfo.getNowProcessStep())
                    .eq(WorkReport::getNowProcessId, workOrderInfo.getNowProcessId()));
            Assert.isTrue(count > 0, "请先报工");
        }
        //获取工艺路线下一道工序
        Long craftRouteId = workOrderInfo.getCraftRouteId();
        CraftProcessRelation craftProcessRelation = craftProcessRelationMapper.selectProcessStep(craftRouteId, workOrderInfo.getNowProcessStep() + 1);
        if (craftProcessRelation != null) {
            workOrderInfo.setNowProcessId(craftProcessRelation.getProcessId());
            workOrderInfo.setNowProcessCode(craftProcessRelation.getProcessCode());
            workOrderInfo.setNowProcessName(craftProcessRelation.getProcessName());
            workOrderInfo.setNowProcessType(craftProcessRelation.getProcessTypeId());
            workOrderInfo.setNowProcessStep(craftProcessRelation.getStep().intValue());
            workOrderInfo.setNowProcessOrderOwnerId(craftProcessRelation.getProcessOrderOwnerId());
        } else {
            workOrderInfo.setStatus(3);
        }
        workOrderInfo.setUpdateBy(userId + "");
        workOrderInfo.setUpdateTime(new Date());
        if (workOrderInfo.getStatus() == 3) {
            workOrderInfo.setActualEndTime(new Date());
            workOrderInfo.setActualQuantity(Optional.ofNullable(actualQuantity).orElse(0) + "");
            workOrderInfo.setScrapQuantity(Optional.ofNullable(scrapQuantity).orElse(0) + "");
        }
        int update = workOrderInfoMapper.updateWorkOrderInfo(workOrderInfo);
        if (update > 0) {
            //更新工单工序完成信息
            workOrderProcessInfoMapper.finishWorkOrderProcess(workOrderId, processData.getId());
            //保存日志
            workOrderLogService.saveLog(workOrderInfo, "工单进度：" + workOrderInfo.getNowProcessName());
            if (workOrderInfo.getStatus() == 3) {
                workOrderLogService.saveLog(workOrderInfo, "工单完工,实际产量："
                        + Optional.ofNullable(actualQuantity).orElse(0)
                        + "，报废数量：" + Optional.ofNullable(scrapQuantity).orElse(0));
                //计划完成
                projectProductionPlanMapper.updateProjectProductionPlan(ProjectProductionPlan.builder()
                        .id(workOrderInfo.getPlanId())
                        .actualEndTime(new Date())
                        .status("5")//已完成
                        .build());
            } else if (craftProcessRelation != null) {
                //添加工单工序信息
                WorkOrderProcessInfo workOrderProcessInfo = WorkOrderProcessInfo.builder()
                        .projectId(workOrderInfo.getProjectId())
                        .projectCode(workOrderInfo.getProjectCode())
                        .planId(workOrderInfo.getPlanId())
                        .planCode(workOrderInfo.getPlanCode())
                        .workOrderCode(workOrderInfo.getWorkOrderCode())
                        .workOrderId(workOrderInfo.getId())
                        .craftId(craftProcessRelation.getCraftId())
                        .step(workOrderInfo.getNowProcessStep())
                        .processId(workOrderInfo.getNowProcessId())
                        .processCode(workOrderInfo.getNowProcessCode())
                        .processName(workOrderInfo.getNowProcessName())
                        .processOrderOwnerId(workOrderInfo.getNowProcessOrderOwnerId())
                        .startTime(new Date())
                        .build();
                workOrderProcessInfo.setCreateBy(userId + "");
                workOrderProcessInfo.setCreateTime(new Date());
                workOrderProcessInfoMapper.insertWorkOrderProcessInfo(workOrderProcessInfo);
            }
        }
        return update;
    }

    @Override
    public Integer finishValidate(Long workOrderId) {
        Assert.notNull(workOrderId, "参数不能为空");
        WorkOrderInfo workOrderInfo = getById(workOrderId);
        Assert.isTrue(workOrderInfo.getStatus() == 3, "工单状态异常");
        //当前领料数量 重新写
        Long applyNum=materialRequisitionMapper.selectMaterialRequisitionNum(workOrderId);
        //已经产出和报废的数量
        String actualQuantity = workOrderInfo.getActualQuantity();
        Integer actualQuantityInt = Optional.ofNullable(actualQuantity).map(Integer::parseInt).orElse(0);
        int scrapQuantityInt = workReportMapper.getListByWorkOrderId(workOrderId)
                .stream()
                .mapToInt(WorkReport::getScrapQuantity)
                .sum();
        //是否存在剩余物料
        if (applyNum > actualQuantityInt + scrapQuantityInt) {
            throw new RuntimeException("存在剩余物料,请退料");
        }
        return 1;
    }

    @Override
    @Transactional
    public int transferOwner(Long workOrderId, Long workOrderOwnerId) {
        Assert.notNull(workOrderId, "工单id不能为空");
        WorkOrderInfo workOrderInfo = getById(workOrderId);
        Assert.notNull(workOrderInfo, "工单不存在");
        Assert.isTrue(workOrderInfo.getStatus() == 2, "工单状态异常");
        Assert.isTrue(!Objects.equals(workOrderOwnerId, workOrderInfo.getWorkOrderOwnerId()), "负责人相同无需转移");
        Long userId = SecurityUtils.getUserId();
        Long oldOwnId = workOrderInfo.getWorkOrderOwnerId();
        boolean isAdmin = SecurityUtils.hasRole("admin");
        //查询项目信息
        Long projectManager = null;
        if (workOrderInfo.getProjectId() != null) {
            ProductionProject productionProject = productionProjectMapper.selectById(workOrderInfo.getProjectId());
            projectManager = productionProject.getProjectManager();
        }
        //是否是项目负责人或者工单负责人
        if (!workOrderInfo.getWorkOrderOwnerId().equals(userId) && !isAdmin) {
            if (projectManager == null || !projectManager.equals(userId)) {
                Assert.isTrue(true, "您没有权限操作");
            }
        }
        workOrderInfo.setWorkOrderOwnerId(workOrderOwnerId);
        workOrderInfo.setUpdateBy(userId + "");
        workOrderInfo.setUpdateTime(new Date());
        int update = workOrderInfoMapper.updateWorkOrderInfo(workOrderInfo);
        if (update > 0) {
            //工序信息转移负责人
            HashMap<Long, String> nameCache = new HashMap<>();
            String nowName = sysUserService.getUserNameByIdAndCache(workOrderOwnerId, nameCache);
            String oldName = sysUserService.getUserNameByIdAndCache(oldOwnId, nameCache);
            //保存日志
            workOrderLogService.saveLog(workOrderInfo, "工单负责人转移：原负责人`" + oldName + "`,变更为`" + nowName + "`");
        }
        return update;
    }

    @Override
    public int transferProcessOwner(Long workOrderId, Long processOwnerId) {
        Assert.notNull(workOrderId, "工单id不能为空");
        WorkOrderInfo workOrderInfo = getById(workOrderId);
        Assert.notNull(workOrderInfo, "工单不存在");
        Assert.isTrue(workOrderInfo.getStatus() == 2, "工单状态异常");
        Assert.isTrue(!Objects.equals(processOwnerId, workOrderInfo.getNowProcessOrderOwnerId()), "负责人相同无需转移");
        Long userId = SecurityUtils.getUserId();
        boolean isAdmin = SecurityUtils.hasRole("admin");
        Long workOrderOwnerId = workOrderInfo.getWorkOrderOwnerId();
        //工单负责人和当前工序负责人才可以完工
        Assert.isTrue(isAdmin || workOrderOwnerId.equals(userId) || Objects.equals(workOrderInfo.getNowProcessOrderOwnerId(), userId), "您没有权限操作");
        Long oldOwnId = workOrderInfo.getNowProcessOrderOwnerId();

        workOrderInfo.setNowProcessOrderOwnerId(processOwnerId);
        workOrderInfo.setUpdateBy(userId + "");
        workOrderInfo.setUpdateTime(new Date());
        int update = workOrderInfoMapper.updateWorkOrderInfo(workOrderInfo);
        if (update > 0) {
            //工序信息转移负责人
            workOrderProcessInfoMapper.transferProcessOwner(workOrderId, workOrderInfo.getNowProcessId(), processOwnerId);
            HashMap<Long, String> nameCache = new HashMap<>();
            String nowName = sysUserService.getUserNameByIdAndCache(processOwnerId, nameCache);
            String oldName = sysUserService.getUserNameByIdAndCache(oldOwnId, nameCache);
            //保存日志
            workOrderLogService.saveLog(workOrderInfo, "工序-" + workOrderInfo.getNowProcessName() + ",负责人转移：原负责人`" + oldName + "`,变更为`" + nowName + "`");
        }
        return update;
    }

    @Override
    @Transactional
    public int cancelByPlain(Long plainId, String source) {
        List<WorkOrderInfo> orderInfoList = lambdaQuery().eq(WorkOrderInfo::getPlanId, plainId)
                .notIn(WorkOrderInfo::getStatus, 3, 4)
                .list();
        if (CollectionUtils.isEmpty(orderInfoList)) {
            log.info("暂无可取消工单");
            return 1;
        }
        for (WorkOrderInfo workOrderInfo : orderInfoList) {
            int cancel = cancel(workOrderInfo.getId(), source);
            Assert.isTrue(cancel > 0, "取消失败");
        }
        return 1;
    }

    @Override
    @Transactional
    public int cancel(Long workOrderId, String source) {
        Assert.notNull(workOrderId, "工单id不能为空");
        WorkOrderInfo workOrderInfo = getById(workOrderId);
        Assert.notNull(workOrderInfo, "工单不存在");
        Assert.isTrue( workOrderInfo.getStatus() != 4, "工单无法作废");
        workOrderInfo.setStatus(4);
        workOrderInfo.setUpdateTime(DateUtils.getNowDate());
        workOrderInfo.setUpdateBy(SecurityUtils.getUserId() + "");
        int update = workOrderInfoMapper.updateWorkOrderInfo(workOrderInfo);
        if (update > 0) {
            String sourceRemark = StringUtils.equals(source, "project") ? "项目作废" : StringUtils.equals(source, "plain") ? "计划作废" : "工单关单";
            workOrderLogService.saveLog(workOrderInfo, "工单关单，关单原因：" + sourceRemark);
            ProjectProductionPlan productionPlan = new ProjectProductionPlan();
            productionPlan.setId(workOrderInfo.getPlanId());
            productionPlan.setStatus("2");
            productionPlan.setUpdateTime(DateUtils.getNowDate());
            update = projectProductionPlanMapper.updateProjectProductionPlan(productionPlan);
            Assert.isTrue(update > 0, sourceRemark+"失败");
        }
        return update;
    }

    @Override
    public Long sumPlainActualQuantity(Long plainId) {
        return workOrderInfoMapper.sumPlainActualQuantity(plainId);
    }

    @Override
    public WorkOrderPrintDTO printWorkOrder(Long workOrderId) {
        WorkOrderInfo workOrderInfo = getById(workOrderId);
        Assert.notNull(workOrderInfo, "工单不存在");
        boolean isSimple = StringUtils.equals(workOrderInfo.getWorkOrderType(), "simple");

        //获取模板
        PrintTemplate printTemplate = printTemplateMapper.selectOne(new LambdaQueryWrapper<PrintTemplate>()
                .eq(PrintTemplate::getPrintType, isSimple ? "workOrder" : "modelProcessAgain")
                .last("limit 1")
                .orderByDesc(PrintTemplate::getId)
        );
        Assert.notNull(printTemplate, "打印模板不存在");


        HashMap<Long, String> nameCache = new HashMap<>();

        String projectOwner = null;
        if (isSimple) {
            ProductionProject productionProject = productionProjectMapper.selectProductionProjectById(workOrderInfo.getProjectId());
            Assert.notNull(productionProject, "项目不存在");
            projectOwner = sysUserService.getUserNameByIdAndCache(productionProject.getProjectManager(), nameCache);

        }
        String workOrderOwner = sysUserService.getUserNameByIdAndCache(workOrderInfo.getWorkOrderOwnerId(), nameCache);

        String remark = workOrderInfo.getRemark();
        //最多100个字超出截取，并在尾部追加....
        if (StringUtils.isNotBlank(remark) && remark.length() > 100) {
            remark = StringUtils.substring(remark, 0, 100) + "...";
        }
        String webSiteUrl = sysConfigService.selectConfigByKey("webSiteUrl");
        Assert.notNull(webSiteUrl, "站点地址未配置");

        List<WorkOrderPrintBomDTO> bomTable = new ArrayList<>();
        List<WorkOrderPrintModelDTO> modelTable = new ArrayList<>();
        if (isSimple) {
            //获取产品bom
            ProductionBom productionBom = productionBomMapper.selectProductionBomById(workOrderInfo.getProductionBomId());
            Assert.notNull(productionBom, "产品BOM不存在");
            MaterialData materialData = materialDataMapper.selectMaterialDataById(productionBom.getMaterialId());
            Assert.notNull(materialData, "物料不存在");
            MaterialCategory category = materialCategoryMapper.selectMaterialCategoryById(materialData.getCategoryId());
            MaterialUnitData materialUnitData = materialUnitDataMapper.selectMaterialUnitDataById(materialData.getMaterialUnit());

            String categoryName = category == null ? "-" : category.getMaterialCategoryName();
            String unitName = materialUnitData == null ? "-" : materialUnitData.getMaterialUnitName();
            long allNum = Optional.ofNullable(productionBom.getDemandQuantity()).orElse(0L) * workOrderInfo.getExpectedQuantity();
            bomTable.add(WorkOrderPrintBomDTO.builder()
                    .level(1)
                    .materialCode(productionBom.getMaterialCode())
                    .materialName(productionBom.getMaterialName())
                    .materialCategory(categoryName)
                    .materialType(productionBom.getMaterialTypeName())
                    .materialSpecification(materialData.getMaterialSpecification())
                    .materialUnitName(unitName)
                    .baseQuantity(Optional.ofNullable(productionBom.getDemandQuantity()).orElse(0L).toString())
                    .allQuantity(Long.toString(allNum))
                    .build());
            List<WorkOrderPrintBomDTO> printBomDTOList = productionBomMaterialMapper.selectProductionBomMaterialList(ProductionBomMaterial.builder().productionBomId(productionBom.getId()).build())
                    .stream()
                    .map(item -> {
                        MaterialData materialDataItem = materialDataMapper.selectMaterialDataById(item.getMaterialId());
                        String materialSpecification = Optional.ofNullable(materialDataItem).map(MaterialData::getMaterialSpecification).orElse("-");
                        MaterialCategory categoryItem = materialCategoryMapper.selectMaterialCategoryById(materialData.getCategoryId());
                        String categoryItemName = categoryItem == null ? "-" : categoryItem.getMaterialCategoryName();
                        long allNumItem = Optional.ofNullable(item.getUsedQuantity()).orElse(0L) * workOrderInfo.getExpectedQuantity();
                        return WorkOrderPrintBomDTO.builder()
                                .level(2)
                                .materialCode(item.getMaterialCode())
                                .materialName(item.getMaterialName())
                                .materialCategory(categoryItemName)
                                .materialType(item.getMaterialTypeName())
                                .materialSpecification(materialSpecification)
                                .materialUnitName(item.getMaterialUnitName())
                                .baseQuantity(Optional.ofNullable(item.getUsedQuantity()).orElse(0L).toString())
                                .allQuantity(Long.toString(allNumItem))
                                .build();
                    })
                    .collect(Collectors.toList());
            bomTable.addAll(printBomDTOList);
        } else {
            Long modelInventoryId = workOrderInfo.getModelInventoryId();
            Assert.notNull(modelInventoryId, "母版库存不存在");
            ModelInventory modelInventory = modelInventoryMapper.selectModelInventoryById(modelInventoryId);
            Assert.notNull(modelInventory, "母版库存不存在");
            String modelSource = DictUtils.getDictLabel("model_source", modelInventory.getModelSource());
            String modelAntiStickType = DictUtils.getDictLabel("model_anti_stick_type", modelInventory.getAntiStickType());
            String warehouseCode = warehouseInfoService.getNameByIdAndCache(modelInventory.getWarehouseId(), new HashMap<>());
            String warehousePosition = warehousePositionService.getByIdAndCache(modelInventory.getWarehousePositionId(), new HashMap<>());
            String modelPrintDetailUrl = sysConfigService.selectConfigByKey("modelPrintDetailUrl");
            Assert.notNull(modelPrintDetailUrl, "母版详情未配置");
            modelTable.add(WorkOrderPrintModelDTO.builder()
                    .qrCode(modelPrintDetailUrl + "?inventoryId=" + modelInventoryId)
                    .modelNumber(modelInventory.getModelNumber())
                    .batchNumber(modelInventory.getBatchNumber())
                    .modelSource(modelSource)
                    .warehouseCode(warehouseCode)
                    .warehousePosition(warehousePosition)
                    .modelMaterial(modelInventory.getModelMaterial())
                    .antiStickType(modelAntiStickType)
                    .antiStickDuration(Optional.ofNullable(modelInventory.getAntiStickDuration()).map(item -> DateFormatUtils.format(item, "yyyy/MM/dd")).orElse("-"))
                    .build());
        }


        CraftRouteVo craftRoute = craftRouteService.selectCraftRouteById(workOrderInfo.getCraftRouteId() + "");
        //工序数据
        List<WorkOrderPrintProcessDTO> workOrderProcessTable = craftRoute.getCraftProcessRelations()
                .stream()
                .map(craftProcessRelation -> {
                    WorkOrderProcessInfo workOrderProcessInfo = workOrderProcessInfoMapper.selectOne(new LambdaQueryWrapper<WorkOrderProcessInfo>()
                            .eq(WorkOrderProcessInfo::getWorkOrderId, workOrderId)
                            .eq(WorkOrderProcessInfo::getProcessId, craftProcessRelation.getProcessId())
                            .last("limit 1")
                            .orderByDesc(WorkOrderProcessInfo::getId)
                    );
                    String optName = workOrderProcessInfo == null ? "-" : sysUserService.getUserNameByIdAndCache(Long.parseLong(workOrderProcessInfo.getCreateBy()), nameCache);
                    List<CraftProcessStationRelation> stationList = craftProcessRelation.getStationList();
                    String deviceName = "-";
                    if (!CollectionUtils.isEmpty(stationList)) {
                        List<Long> stationIdList = stationList.stream().map(CraftProcessStationRelation::getStationId).collect(Collectors.toList());
                        //获取设备信息
                        if (!CollectionUtils.isEmpty(stationIdList)) {
                            deviceName = equipmentDataMapper.selectEquipmentDataListByStationIdList(stationIdList)
                                    .stream()
                                    .map(EquipmentData::getEquipmentName)
                                    .collect(Collectors.joining(","));
                        }
                    }
                    return WorkOrderPrintProcessDTO.builder()
                            .index(craftProcessRelation.getStep().intValue())
                            .qrCode(webSiteUrl + "/crafts/process-data-add?id=" + craftProcessRelation.getProcessId() + "&isView=1")
                            .processCodeAndName(craftProcessRelation.getProcessCode() + "\n" + craftProcessRelation.getProcessName())
                            .startTime(workOrderProcessInfo == null || workOrderProcessInfo.getStartTime() == null ? "-" : DateFormatUtils.format(workOrderProcessInfo.getStartTime(), "yyyy-MM-dd HH:mm:ss"))
                            .endTime(workOrderProcessInfo == null || workOrderProcessInfo.getEndTime() == null ? "-" : DateFormatUtils.format(workOrderProcessInfo.getEndTime(), "yyyy-MM-dd HH:mm:ss"))
                            .device(deviceName)
                            .optName(optName)
                            .build();
                })
                .collect(Collectors.toList());
        return WorkOrderPrintDTO.builder()
                .template(printTemplate.getContent())
                .templateName(isSimple ? "生产工单" : "母版加工单")
                .qrCode(webSiteUrl + "/productionmanage/workOrderInfoView?id=" + workOrderId)
                .workOrderCode("No." + workOrderInfo.getWorkOrderCode())
                .workOrderNum(workOrderInfo.getWorkOrderCode())
                .projectCodeNum(workOrderInfo.getProjectCode())
                .materialCodeNum(workOrderInfo.getMaterialCode())
                .materialName(workOrderInfo.getMaterialName())
                .plainNum(workOrderInfo.getExpectedQuantity() + "")
                .materialUnitName(workOrderInfo.getMaterialUnitName())
                .plainEndTime(DateFormatUtils.format(workOrderInfo.getPlannedStartTime(), "yyyy-MM-dd HH:mm:ss"))
                .plainStartTime(DateFormatUtils.format(workOrderInfo.getPlannedEndTime(), "yyyy-MM-dd HH:mm:ss"))
                .workOrderOwner(workOrderOwner)
                .projectOwner(projectOwner)
                .remark(remark)
                .bomTable(bomTable)
                .modelTable(modelTable)
                .processTable(workOrderProcessTable)
                .build();
    }
}